﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Sofire.DataComm.Net
{
    /// <summary>
    /// 一个可靠套接字的实现。
    /// </summary>
    public abstract class SocketBase : CommunicationBase
    {
        /// <summary>
        /// 设置或获取默认缓冲区字节大小。
        /// </summary>
        public static int DefaultBufferLength = 8192;
        /// <summary>
        /// 设置或获取默认缓冲区字节大小（<see cref="System.Int64"/> 类型）。
        /// </summary>
        public static long DefaultBufferLengthL = SocketBase.DefaultBufferLength;
        /// <summary>
        /// 此连接由 .NET Framework 或基础套接字提供程序中止的异常。
        /// </summary>
        public static readonly SocketException ConnectionAbortedException = new SocketException((int)SocketError.ConnectionAborted);

        #region Public Fields

        /// <summary>
        /// 异步接收的委托。
        /// </summary>
        protected readonly AsyncCallback BeginReceiveCallback;
        /// <summary>
        /// 异步发送的委托。
        /// </summary>
        protected readonly AsyncCallback BeginSendCallback;

        /// <summary>
        /// 可靠的套接字接收完整数据后发生。
        /// </summary>
        public event ReceivedEventHandler Received;
        /// <summary>
        /// 可靠的套接字接收数据后发生。
        /// </summary>
        public event ReceivingEventHandler Receiving;
        /// <summary>
        /// 可靠的套接字接收完整数据时抛出的异常。当发生异常后，通讯将停止接收。
        /// </summary>
        public event SocketExceptionEventHandler SocketFaild;

        /// <summary>
        /// 可靠的套接字发送完整数据后发生。
        /// </summary>
        public event SentEventHandler Sent;

        #endregion

        #region Receive

        /// <summary>
        /// 可靠的套接字 接收数据 或 发送数据 时抛出的异常。当发生异常后，<paramref name="fromSocket"/> 将停止接收。
        /// </summary>
        /// <param name="fromSocket">数据来源的 <see cref="System.Net.Sockets.Socket"/>。</param>
        /// <param name="exception">接收时抛出的异常。</param>
        protected virtual void OnSocketFaild(Socket fromSocket, Exception exception)
        {
            if(this.SocketFaild != null) this.SocketFaild(this, new SocketExceptionEventArgs(fromSocket, exception));
        }

        private void OnBeginReceive(IAsyncResult ar)
        {
            if(!this.IsRunning) return;

            ReceiveStateObject state = ar.AsyncState as ReceiveStateObject;
            var remoteSocket = state.RemoteSocket;
            int receiveLength;
            try
            {
                receiveLength = state.RemoteSocket.EndReceive(ar);
            }
            catch(Exception ex)
            {
                this.OnSocketFaild(remoteSocket, ex);
                return;
            }

            if(receiveLength == 0)
            {
                this.OnSocketFaild(remoteSocket, ConnectionAbortedException);
                return;
            }

            if(this.RaiseReceiving(state.RemoteSocket, state.Buffer))
            {
                state.Add(receiveLength);

                if(receiveLength != DefaultBufferLength)
                {
                    this.RaiseReceived(state.RemoteSocket, state.MergeBytes());
                }
            }

            Array.Clear(state.Buffer, 0, receiveLength);

            this.BeginReceive(state);
        }

        /// <summary>
        /// 指定自定义数据，开始异步接收数据。
        /// </summary>
        /// <param name="state">接收的自定义数据。</param>
        public virtual void BeginReceive(ReceiveStateObject state)
        {
            if(!this.IsRunning) return;
            var socket = state.RemoteSocket;
            try
            {
                state.Buffer = new byte[DefaultBufferLength];
                socket.BeginReceive(state.Buffer, 0, SocketBase.DefaultBufferLength, SocketFlags.None, this.BeginReceiveCallback, state);

            }
            catch(Exception ex)
            {
                this.OnSocketFaild(socket, ex);
            }

        }

        /// <summary>
        /// 指定接收端，开始异步接收数据。
        /// </summary>
        /// <param name="fromSocket">数据来源的 <see cref="System.Net.Sockets.Socket"/>。</param>
        public void BeginReceive(Socket fromSocket)
        {
            this.BeginReceive(new ReceiveStateObject(fromSocket));
        }

        /// <summary>
        /// 可靠的套接字接收完整数据后发生的方法。
        /// </summary>
        /// <param name="fromSocket">发送字节数据的 <see cref="System.Net.Sockets.Socket"/>。</param>
        /// <param name="data">接收的所有字节数据。</param>
        protected virtual void RaiseReceived(Socket fromSocket, byte[] data)
        {
            if(this.Received != null) this.Received(this, new ReceivedEventArgs(fromSocket, data));
        }

        /// <summary>
        /// 可靠的套接字接收数据后发生的方法。
        /// </summary>
        /// <param name="fromSocket">发送字节数据的 <see cref="System.Net.Sockets.Socket"/>。</param>
        /// <param name="data">接收的所有字节数据。</param>
        protected virtual bool RaiseReceiving(Socket fromSocket, byte[] data)
        {
            if(this.Receiving != null)
            {
                var e = new ReceivingEventArgs(fromSocket, data);
                this.Receiving(this, e);
                return e.KeepBuffer;
            }
            return true;
        }

        #endregion

        #region Send

        /// <summary>
        /// 包装待发送的字节数组。
        /// </summary>
        /// <param name="buffer">待发送的字节数组。</param>
        /// <returns>返回包装后的字节数组。</returns>
        public virtual byte[] PackageBytes(byte[] buffer)
        {
            // 假设传递 2048 个字节，则默认在尾巴增加一个 0
            // 2048%1024 = 0，服务端原本接收 2
            // 2049，服务端需要接收3次
            long longLength = buffer.LongLength;
            if(longLength % DefaultBufferLengthL == 0L)
            {
                byte[] temp = new byte[longLength + 1L];
                buffer.CopyTo(temp, 0);
                temp[longLength] = 0;
                buffer = temp;
            }
            return buffer;
        }

        private void OnBeginSend(IAsyncResult ar)
        {
            if(!this.IsRunning) return;

            var sendState = ar.AsyncState as SendStateObject;

            var socket = sendState.RemoteSocket;
            int sentLength = -1;
            Exception exception = null;
            try
            {
                sentLength = socket.EndSendTo(ar);
            }
            catch(Exception ex)
            {
                exception = ex;
            }

            this.RaiseSent(sendState, sentLength, exception);
        }

        /// <summary>
        /// 可靠的套接字发送完整数据后发生。
        /// </summary>
        /// <param name="state">发送的自定义数据。</param>
        /// <param name="sentLength">获取发送数据的字节长度。</param>
        /// <param name="exception">表示发送的异常信息。可以为 null 值，表示这是一个成功的发送结果。</param>
        protected virtual void RaiseSent(SendStateObject state, int sentLength, Exception exception)
        {
            if(state.RaiseSent(sentLength, exception))
            {
                if(this.Sent != null) this.Sent(this, new SentEventArgs(state.RemoteSocket, sentLength, state.Tag, exception));
            }

            if(exception != null)
            {
                this.OnSocketFaild(state.RemoteSocket, exception);
            }
        }

        /// <summary>
        /// 指定字节数组和接收端，开始异步发送数据。
        /// </summary>
        /// <param name="buffer">字节数组。</param>
        /// <param name="remoteSocket">接收数据的远程端。</param>
        public void BeginSendTo(byte[] buffer, Socket remoteSocket)
        {
            this.BeginSendTo(new SendStateObject(remoteSocket, buffer, null));
        }

        /// <summary>
        /// 指定字节数组和接收端，开始异步发送数据。
        /// </summary>
        /// <param name="buffer">字节数组。</param>
        /// <param name="remoteSocket">接收数据的远程端。</param>
        /// <param name="tag">用户自定义数据。</param>
        public void BeginSendTo(byte[] buffer, Socket remoteSocket, object tag)
        {
            if(!this.IsRunning) return;
            this.BeginSendTo(new SendStateObject(remoteSocket, buffer, tag));
        }

        /// <summary>
        /// 指定发送自定义数据，开始异步发送数据。
        /// </summary>
        /// <param name="state">发送的自定义数据。</param>
        public void BeginSendTo(SendStateObject state)
        {
            if(state == null) throw new ArgumentNullException("state");
       
            var socket = state.RemoteSocket;
            if(socket.Connected)
            {
                var buffer = this.PackageBytes(state.Buffer);
                try
                {
                    socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, this.BeginSendCallback, state);
                }
                catch(Exception ex)
                {
                    this.RaiseSent(state, -1, ex);
                }
            }
            else
            {
                this.RaiseSent(state, -1, ConnectionAbortedException);
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// 初始化 <see cref="Sofire.DataComm.Net.SocketBase"/> 的新实例。
        /// </summary>
        /// <param name="socketInfo">套接字的基础信息。</param>
        public SocketBase(SocketInfo socketInfo)
        {
            if(socketInfo == null) throw new ArgumentNullException("socketInfo");
            this._SocketInfo = socketInfo;
            BeginReceiveCallback = new AsyncCallback(OnBeginReceive);
            BeginSendCallback = new AsyncCallback(OnBeginSend);
        }

        /// <summary>
        /// 套接字的基础信息。
        /// </summary>
        protected SocketInfo _SocketInfo;
        /// <summary>
        /// 获取套接字的基础信息。
        /// </summary>
        public SocketInfo SocketInfo
        {
            get
            {
                return this._SocketInfo;
            }
        }

        #endregion
    }
}
